package com.mp3.player.managers
{
	import com.mp3.player.events.TrackEvent;
	import com.mp3.player.vos.Track;
	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.media.SoundTransform;
	import flash.net.URLRequest;
	
	import mx.controls.Alert;

	/**
	 *  Dispatched when the playing of track is finished.
	 *
	 *  @eventType com.mp3.player.events.TrackEvent.FINISH
	 */
	[Event(name="finish", type="com.mp3.player.events.TrackEvent")]
	
	/**
	 *  Dispatched when track info getted from sound and setted to track.
	 *
	 *  @eventType com.mp3.player.events.TrackEvent.INFO_CHANGED
	 */
	[Event(name="infoChanged", type="com.mp3.player.events.TrackEvent")]
	
	/**
	 * Class that manage loading and playing tracks. 
	 */	
	public class TrackManager extends EventDispatcher
	{
		/**
		 * Constructor. 
		 */		
		public function TrackManager()
		{
		}
		
		/**
		 * @private
		 *  
		 * Sound.
		 */		
		private var sound:Sound;
		
		/**
		 * @private
		 *  
		 * Sound chanel.
		 */	
		private var channel:SoundChannel;

		[Bindable]
		/**
		 * Flag that indicates whether current track is playing. 
		 */		
		public var isPlaying:Boolean = false;
		
		[Bindable]
		/**
		 * Total bytes in track. 
		 */		
		public var bytesTotal:Number = 0;
		
		[Bindable]
		/**
		 * Loaded bytes. 
		 */	
		public var bytesLoaded:Number = 0;
		
		/**
		 * Storage for volume.
		 */		
		private var _volume:Number = 1;
		
		/**
		 * @private
		 * 
		 * Current track. 
		 */		
		private var track:Track;

		[Bindable]
		/**
		 * Current track volume. 
		 */		
		public function get volume():Number
		{
			return _volume;
		}
		
		/**
		 * @private
		 */
		public function set volume(value:Number):void
		{
			if(_volume != value)
			{
				_volume = value;
				
				if(channel)
				{
					var transform:SoundTransform = channel.soundTransform;
					transform.volume = value;
					channel.soundTransform = transform;
				}
			}
		}

		/**
		 * Current playing position (miliseconds).  
		 */		
		public function get currentPosition():Number
		{
			return channel ? channel.position : 0;
		}
		
		/**
		 * Duration of track (miliseconds). 
		 */	
		public function get trackLength():Number
		{
			return sound ? sound.length*bytesTotal/bytesLoaded: 0;
		}

		//--------------------------------------------------------------------------
		//
		//  Methods
		//
		//--------------------------------------------------------------------------
		/**
		 * Load track.
		 *  
		 * @param track Track
		 * 
		 */		
		public function loadSound(track:Track):void
		{
			this.track = track;

			if(channel) 
			{
				channel.stop();
				channel.removeEventListener(Event.SOUND_COMPLETE,  soundCompleteHandler);
				channel = null;
			}
			if(sound)
			{
				sound.removeEventListener(IOErrorEvent.IO_ERROR, errorHandler);
				sound.removeEventListener(ProgressEvent.PROGRESS, progressHandler);
				sound.removeEventListener(Event.ID3, onID3);
				if(sound.isBuffering)
					sound.close();
				sound = null;
			}
			
			isPlaying = false;
			
			if(track)
			{
				sound = new Sound()
				sound.addEventListener(IOErrorEvent.IO_ERROR, errorHandler);
				sound.addEventListener(ProgressEvent.PROGRESS, progressHandler);
				sound.addEventListener(Event.ID3, onID3);
				
				var request:URLRequest = new URLRequest(track.url);
				sound.load(request)
					
				dispatchChangeTrack();
			}
		}
		
		/**
		 * Play track.
		 *  
		 * @param position Number. If NaN then resume playing from current position.
		 * 
		 */		
		public function play(position:Number = NaN):void
		{
			position = isNaN(position) ? currentPosition : position;
			
			if(channel) 
			{
				channel.stop();
				channel.removeEventListener(Event.SOUND_COMPLETE,  soundCompleteHandler);
				channel = null;
			}
			
			
			
			if(sound)
			{
				channel = sound.play(position);
				var transform:SoundTransform = channel.soundTransform;
				transform.volume = _volume;
				channel.soundTransform = transform;
				channel.addEventListener(Event.SOUND_COMPLETE,  soundCompleteHandler);
				isPlaying = true;
			}
		}
		
		/**
		 * Pause track. 
		 */		
		public function pause():void
		{
			if(channel)
				channel.stop();
			
			isPlaying = false;
		}
		
		/**
		 * Pause track. 
		 */		
		public function stop():void
		{
			if(channel)
			{
				channel.stop();
				channel.removeEventListener(Event.SOUND_COMPLETE,  soundCompleteHandler);
			}
			
			channel = null;
			
			if(sound)
			{
				sound.removeEventListener(IOErrorEvent.IO_ERROR, errorHandler);
				sound.removeEventListener(ProgressEvent.PROGRESS, progressHandler);
				sound.removeEventListener(Event.ID3, onID3);
				if(sound.isBuffering)
					sound.close();
				sound = null;
			}
			
			track = null;
			
			isPlaying = false;
		}
		
		//--------------------------------------------------------------------------
		//
		//  Event handlers
		//
		//--------------------------------------------------------------------------
		/**
		 * @private
		 * 
		 * Error handler
		 * 
		 * @param event IOErrorEvent
		 */		
		private function errorHandler(event:IOErrorEvent):void
		{
			dispatchEvent(new TrackEvent(TrackEvent.FINISH, track));
		}
		
		/**
		 * @private
		 * 
		 * Progress handler.
		 * 
		 * @param event ProgressEvent
		 */		
		private function progressHandler(event:ProgressEvent):void
		{
			bytesTotal = event.bytesTotal;
			bytesLoaded = event.bytesLoaded;
		}
		
		/**
		 * @private
		 * 
		 * Sound complete handler.
		 * 
		 * @param event Event
		 */		
		private function soundCompleteHandler(event:Event):void
		{
			dispatchEvent(new TrackEvent(TrackEvent.FINISH, track));
		}
		
		/**
		 * Sound ID3 handler
		 * @param event
		 */		
		private function onID3(event: Event = null): void
		{
			if (sound.id3 != null)
			{
				track.id3 = sound.id3;
				
				dispatchChangeTrack();
			}
		}
		
		/**
		 * Function to dispatch change track event 
		 */		
		private function dispatchChangeTrack(): void
		{
			var trackEvent: TrackEvent = new TrackEvent(TrackEvent.INFO_CHANGED);
			trackEvent.track = track;
			dispatchEvent(trackEvent);
		}
	}
}